﻿using System;
using System.Drawing;
using System.IO;
using System.Text;
using System.Linq;
using System.DrawingCore;
using System.Collections.Generic;
using MongoDB.Driver;
using MongoDB.Driver.Linq;
using log4net;
using GXJD.Wx.DBContext;
using Microsoft.EntityFrameworkCore;

namespace GXJD.NewsPush
{
    class Program
    {
        #region 配置日志模块
        private static log4net.Repository.ILoggerRepository repository = LogManager.CreateRepository("GXJD.NewsPush");
        internal static ILog Logger;
        #endregion

        #region 程序用到的常量定义
        /// <summary>
        /// 时间间隔（秒）
        /// </summary>
        private static int timerIntervalSeconds = 30;
        /// <summary>
        /// 消息要推送的QQ好友的备注列表
        /// </summary>
        private static List<string> MessagePushUserList = new List<string>() { "梁梦雪" };
        /// <summary>
        /// 允许获取数据时间段的开始时间
        /// </summary>
        private static string bgnTimeStr = "08:30";
        /// <summary>
        /// 允许获取数据时间段的结束时间
        /// </summary>
        private static string endTimeStr = "20:30";

        /// <summary>
        /// 显示列表的URL
        /// </summary>
        private static string urlView = @"http://www.gxbidding.com/globalnews/News";
        /// <summary>
        /// 业务系统数据库连接字符串
        /// </summary>
        private static string ywxtConnString = @"User ID=gxjd;Password=gxjd123456;Host=192.168.99.4;Port=5432;Database=gxjd_ywxt;Pooling=true";

        /// <summary>
        /// MongoDB数据库服务器IP地址
        /// </summary>
        private static string MongoServerIP = @"192.168.6.65";

        /// <summary>
        /// MongoDB数据库服务器端口
        /// </summary>
        private static int MongoServerPort = 27017;

        /// <summary>
        /// MongoDB数据库服务器数据名称
        /// </summary>
        private static string MongoDatabaseName = @"news";

        /// <summary>
        /// MongoDB数据库服务器登陆用户
        /// </summary>
        private static string MongoUser = @"user1";

        /// <summary>
        /// MongoDB数据库服务器登陆密码
        /// </summary>
        private static string MongoPassword = @"123456789";
        #endregion

        static void Main(string[] args)
        {
            Console.Clear();
            Console.Title = "网站消息推送";

            // 默认简单配置，输出至控制台
            //log4net.Config.BasicConfigurator.Configure(repository);
            // XML配置，输出至控制台
            //log4net.Config.XmlConfigurator.Configure(repository, new FileInfo("log.config"));
            Logger = LogManager.GetLogger(repository.Name, "GXJD.NewsPush.Log");

            log4net.Config.XmlConfigurator.Configure(repository, new FileInfo("log.config"));

            Console.CancelKeyPress += new ConsoleCancelEventHandler(CtrlC_Handler);

            Console.Clear();
            Console.WriteLine("服务运行中，Ctrl+C或者按X键退出服务程序......");

            //*/
            Logger.Info("定时推送程序启动！");
            Logger.Info("按 X 或者 CTRL+C 退出程序。");

            //创建定时器
            System.Timers.Timer myTimer = new System.Timers.Timer()
            {
                Interval = timerIntervalSeconds * 1000
            };
            myTimer.Elapsed += new System.Timers.ElapsedEventHandler(myTimerEvent);
            myTimer.Start();

            while (true)
            {
                // 循环等待用户输入
                var cki = Console.ReadKey(true);

                // 按 'X' 键退出.
                if (cki.Key == ConsoleKey.X)
                {
                    Logger.Info("用户按X键退出。");
                    break;
                }
            }

            Environment.Exit(1);
        }

        /// <summary>
        /// Ctrl+C按键的处理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        protected static void CtrlC_Handler(object sender, ConsoleCancelEventArgs args)
        {
            args.Cancel = false;
            Logger.Info("用户按Ctrl+C键退出。");
            Environment.Exit(1);
        }

        /// <summary>
        /// 定时器事件，操作步骤：
        /// 1、获取MongoDB中未处理的新闻列表
        /// 2、转存到协同新闻列表数据库
        /// 3、推送信息到邮件
        /// 4、更新MongoDB中的新闻列表数据的isPushed标志
        /// 5、以上都没有问题，则COMMIT业务系统数据库
        /// 6、如果有问题，回滚业务系统数据库
        /// </summary>
        /// <param name="source"></param>
        /// <param name="e"></param>
        public static void myTimerEvent(object sender, System.Timers.ElapsedEventArgs e)
        {
            try
            {
                (sender as System.Timers.Timer).Stop();

                #region 时间处理
                var time_bgn = DateTime.Parse(DateTime.Today.ToString("yyyy-MM-dd") + " " + bgnTimeStr);
                var time_end = DateTime.Parse(DateTime.Today.ToString("yyyy-MM-dd") + " " + endTimeStr);
                var time_now = DateTime.Now;
                if (time_now < time_bgn || time_now > time_end)
                {
                    if (time_now < time_bgn.AddSeconds(-10 * timerIntervalSeconds)
                        || time_now > time_end.AddSeconds(10 * timerIntervalSeconds))
                    {
                        //距离开始时间还早，修改计时器间隔为正常时间间隔的10倍
                        (sender as System.Timers.Timer).Interval = 10 * timerIntervalSeconds * 1000;
                    }
                    else
                    {
                        //距离开始时间将近，修改计时器间隔为正常的时间间隔
                        (sender as System.Timers.Timer).Interval = timerIntervalSeconds * 1000;
                    }
                }
                #endregion

                #region 初始化业务系统数据库访问
                var _connString = ywxtConnString;
                var _contextOptions = new DbContextOptionsBuilder()
                    .UseNpgsql(_connString)
                    .Options;
                #endregion

                using (var db = new WxDBContext(_contextOptions))
                using (var tran = db.Database.BeginTransaction())
                {
                    //处理操作
                    try
                    {
                        var _r = new CommonResult();

                        #region 1、获取MongoDB中未处理的新闻列表
                        var _news = getNews();
                        Logger.Info("本次获取新闻列表数量为" + _news.Count.ToString());
                        if (_news.Count == 0) return;
                        #endregion

                        #region 2、转存新闻列表到协同数据库
                        _r = saveNewsToYwxt(db, _news);
                        if (!_r.ISOK)
                        {
                            Logger.Error("运行错误，错误代码：" + _r.MESSAGE);
                            tran.Rollback();
                            return;
                        }
                        #endregion

                        #region 3、获取邮件推送的配置
                        var _sites = (from s in db.NewsWebSites
                                      where s.isvalid == 1
                                      select s).ToList();
                        #endregion

                        #region 4、根据邮件推送的网站配置，逐一发送邮件给相应的接收人
                        foreach (var _s in _sites)
                        {

                            var _this_site_news = _news.FindAll(c => c.webSite.Contains(_s.sitename)).OrderBy(c => c.webSite).ToList();
                            if (_this_site_news.Count > 0)
                            {
                                var _gx_title = "【网站新内容提示】有" + _this_site_news.Count.ToString() + @"条新的网站内容推送，请及时查阅。";
                                var _gx_content = "<h3>" + _gx_title + @"</h3>";

                                var _header = string.Empty;
                                foreach (var _n in _this_site_news)
                                {
                                    if (_header != _n.webSite)
                                    {
                                        //输出列表头（网站名称）
                                        _gx_content += @"
<p style='padding-top:1.5em;'><strong>" + _n.webSite + "</strong></p>";
                                        _header = _n.webSite;
                                    }

                                    _gx_content += @"
<p><a href='" + _n.url_f + @"'>" + _n.title + "</a></p>";
                                }

                                _gx_content += @"
<p>点击<a href='" + urlView + "'>这里</a>查看完整新闻列表。</p>";

                                List<string> _to_users = _s.tousers.Split(
                                    new char[] { ',', ';', '|' },
                                    StringSplitOptions.RemoveEmptyEntries).ToList();

                                //发送邮件
                                _r = pushMessageToUserMail(
                                    db,
                                    _gx_title,
                                    _gx_content,
                                    _to_users);
                                if (!_r.ISOK)
                                {
                                    Logger.Error("发送消息提醒时出现错误，错误代码：" + _r.MESSAGE);
                                    tran.Rollback();
                                    return;
                                }

                            }
                        }
                        #endregion

                        #region 5、更新MongoDB中的新闻列表数据的isPushed标志
                        _r = UpdateNews(_news);
                        if (!_r.ISOK)
                        {
                            Logger.Error("发送消息提醒后，更新MongoDB推送标志时出现错误，错误代码：" + _r.MESSAGE);
                            tran.Rollback();
                            return;
                        }
                        #endregion

                        db.SaveChanges();

                        tran.Commit();
                    }
                    catch (Exception ex)
                    {
                        Logger.Error("运行错误，错误代码：" + ex.Message);
                        tran.Rollback();
                    }
                }
            }
            finally
            {
                //重新启动计时器
                (sender as System.Timers.Timer).Start();
            }

        }

        /// <summary>
        /// 返回Mongo数据库中未处理的新闻列表
        /// </summary>
        /// <returns></returns>
        private static List<Models.News> getNews()
        {
            var _returnValue = new List<Models.News>();

            //要发送的消息正文
            var toContent = string.Empty;

            //连接MogoDB的配置项
            var settings = new MongoClientSettings
            {
                Credential = MongoCredential.CreateCredential(MongoDatabaseName, MongoUser, MongoPassword),
                Server = new MongoServerAddress(MongoServerIP, MongoServerPort),
                MaxConnectionIdleTime = new TimeSpan(0, 3, 0)
            };
            var mongoclient = new MongoClient(settings);

            if (mongoclient == null)
            {
                throw new Exception("Mongo数据库未能连接成功。");
            }

            //连接数据库
            var _database = mongoclient.GetDatabase(MongoDatabaseName);

            var _collection_news = _database.GetCollection<Models.News>("news");
            return _returnValue = (
                from _news in _collection_news.AsQueryable()
                where _news.isPushed != "1"
                orderby _news.date descending
                select _news)
                .ToList();
        }

        /// <summary>
        /// 修改Mongo数据库中未处理的新闻列表
        /// </summary>
        /// <returns></returns>
        private static CommonResult UpdateNews(List<Models.News> data)
        {
            #region 0、初始化参数定义以及数据库链接定义
            //连接MogoDB的配置项
            var settings = new MongoClientSettings
            {
                Credential = MongoCredential.CreateCredential(MongoDatabaseName, MongoUser, MongoPassword),
                Server = new MongoServerAddress(MongoServerIP, MongoServerPort),
                MaxConnectionIdleTime = new TimeSpan(0, 3, 0)
            };

            var mongoclient = new MongoClient(settings);

            if (mongoclient == null)
            {
                return new CommonResult("Mongo数据库未能连接成功。");
            }

            //连接数据库
            var _database = mongoclient.GetDatabase(MongoDatabaseName);
            #endregion

            #region 1、逐一修改新闻列表的isPushed标志
            try
            {
                var _collection_news = _database.GetCollection<Models.News>("news");

                var _query = (from c in _collection_news.AsQueryable()
                              select c);

                data.ForEach(_item =>
                {
                    _collection_news.UpdateOneAsync(
                            //要修改的记录的查询条件
                            (e => e._id == _item._id),
                            //要修改的字段Set
                            Builders<Models.News>
                            .Update
                            .Set(c => c.isPushed, "1")
                            .Set(c => c.meta.updatedAt, DateTime.Now)
                        );
                });
            }
            catch (Exception ex)
            {
                return new CommonResult(ex.Message);
            }
            #endregion

            return new CommonResult();
        }

        /// <summary>
        /// 将新闻列表保存到业务系统数据库中
        /// </summary>
        /// <param name="_db">数据库连接</param>
        /// <param name="_news">要保存的新闻列表</param>
        private static CommonResult saveNewsToYwxt(
            WxDBContext _db, 
            List<Models.News> _news)
        {
            if (_db.Database.CurrentTransaction == null)
            {
                return new CommonResult("数据库处理事务未启动！");
            }

            foreach (var _n in _news)
            {
                var _ywxt_news = new Wx.DBModel.News()
                {
                    content = _n.content,
                    date = _n.date,
                    sitename = _n.webSite,
                    title = _n.title,
                    url = _n.url_f
                };

                _db.News.Add(_ywxt_news);
            }

            return new CommonResult();
        }

        /// <summary>
        /// 推送提醒消息给用户的EMail
        /// </summary>
        /// <param name="_message">要推送的消息</param>
        /// <param name="_emails">接收消息的用户昵称列表</param>
        private static CommonResult pushMessageToUserMail(
            WxDBContext _db,
            string _title,
            string _content,
            List<string> _emails)
        {
            //拼凑收件人
            var _to_users = string.Join(',', _emails.ToArray());

            //新增加邮件记录
            var _mail = new Wx.DBModel.ApprovalMail()
            {
                Content = _content,
                IsSend = 0,
                MailCC = string.Empty,
                MailTo = _to_users,
                PlanDate = DateTime.Now,
                SendedDate = DateTime.MinValue,
                Title = _title,
                ToDoId = string.Empty,
                UserID = string.Empty
            };

            _db.Mails.Add(_mail);

            return new CommonResult();
        }
    }
}
